﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Threading;
using System.Diagnostics;
using System.IO.IsolatedStorage;
using System.Windows;

namespace Passwords.DAL
{
    internal static class DALUtil
    {

        public static string AsString(this SecureString secure)
        {
            IntPtr ptr = IntPtr.Zero;
            try
            {
                ptr = Marshal.SecureStringToCoTaskMemUnicode(secure);
                string password = Marshal.PtrToStringUni(ptr);
                return password;
            }
            finally
            {
                if (ptr != IntPtr.Zero) Marshal.ZeroFreeGlobalAllocUnicode(ptr);
            }
        }

        public static byte[] ComputeHash(this SecureString password)
        {
            int len = password.Length * 2;

            byte[] buffer = new byte[len];
            byte[] hash = null;
            IntPtr ptr = IntPtr.Zero;

            try
            {
                ptr = Marshal.SecureStringToGlobalAllocUnicode(password);

                Marshal.Copy(ptr, buffer, 0, len);
                using (HMACSHA256 sha = new HMACSHA256(UnicodeEncoding.Unicode.GetBytes("??%Sync##Service+++")))
                {
                    //string s = Marshal.PtrToStringUni(ptr);
                    //Trace.WriteLine(s);
                    hash = sha.ComputeHash(buffer);
                }
            }
            finally
            {
                if (ptr != IntPtr.Zero) Marshal.ZeroFreeGlobalAllocUnicode(ptr);
                for (int i = 0; i < len; i++) buffer[i] = 0;
            }

            return hash;
        }

        public static byte[] ComputeHash(this string password)
        {
            if (password == null) password = string.Empty;
            using (HMACSHA256 sha = new HMACSHA256(UnicodeEncoding.Unicode.GetBytes("??%Sync##Service+++")))
            {
                byte[] hash = sha.ComputeHash(UnicodeEncoding.Unicode.GetBytes(password));
                return hash;
            }
        }


        public static bool IsEqual(byte[] a, byte[] b)
        {
            if (a == null && b == null) return true;
            if (a == null || b == null) return false;
            if (a.Length != b.Length) return false;

            for (int i = 0; i < a.Length; i++) if (a[i] != b[i]) return false;
            return true;
        }

        public static void CreateCryptoKey(string password, out byte[] cryptoKey, out byte[] cryptoIV)
        {
            Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(password, UnicodeEncoding.Unicode.GetBytes("PasswordsSaltValue"),1000);
            cryptoKey = rfc.GetBytes(32);
            cryptoIV = rfc.GetBytes(16);
        }

        public static void CreateCryptoKey(SecureString password, out byte[] cryptoKey, out byte[] cryptoIV)
        {
            IntPtr ptr = IntPtr.Zero;
            byte[] bytes = null;
            try
            {
                //ptr = Marshal.SecureStringToGlobalAllocAnsi(password);
                ptr = Marshal.SecureStringToGlobalAllocAnsi(password);
                int len = password.Length * 1;
                bytes = new byte[len];
                Marshal.Copy(ptr, bytes, 0, len);
                Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(bytes, UnicodeEncoding.Unicode.GetBytes("PasswordsSaltValue"), 1000);
                cryptoKey = rfc.GetBytes(32);
                cryptoIV = rfc.GetBytes(16);
            }
            finally
            {
                if (bytes != null) for (int i = 0; i < bytes.Length; i++) bytes[i] = 0;
                if (ptr != IntPtr.Zero) Marshal.ZeroFreeGlobalAllocAnsi(ptr);
            }
        }

        public static void CreateCryptoKeyUni(SecureString password, out byte[] cryptoKey, out byte[] cryptoIV)
        {
            IntPtr ptr = IntPtr.Zero;
            byte[] bytes = null;
            try
            {
                //ptr = Marshal.SecureStringToGlobalAllocAnsi(password);
                ptr = Marshal.SecureStringToGlobalAllocUnicode(password);
                int len = password.Length * 2;
                bytes = new byte[len];
                Marshal.Copy(ptr, bytes, 0, len);
                Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(bytes, UnicodeEncoding.Unicode.GetBytes("PasswordsSaltValue"), 1000);
                cryptoKey = rfc.GetBytes(32);
                cryptoIV = rfc.GetBytes(16);
            }
            finally
            {
                if (bytes != null) for (int i = 0; i < bytes.Length; i++) bytes[i] = 0;
                 if (ptr != IntPtr.Zero) Marshal.ZeroFreeGlobalAllocUnicode(ptr);
            }
        }

        internal static void Zero(this byte[] key)
        {
            if (key != null)
            {
                for (int i = 0; i < key.Length; i++) key[i] = 0;
            }
        }

        internal static bool IsEqual(SecureString a, SecureString b)
        {
            if (a.Length != b.Length) return false;
            int len = a.Length;
            byte[] mem1 = new byte[len];
            byte[] mem2 = new byte[len];

            IntPtr ptr1 = Marshal.SecureStringToGlobalAllocAnsi(a);
            IntPtr ptr2 = Marshal.SecureStringToGlobalAllocAnsi(b);
            Marshal.Copy(ptr1, mem1, 0, len);
            Marshal.Copy(ptr2, mem2, 0, len);
            return DALUtil.IsEqual(mem1, mem2);
        }

        /// <summary>
        /// Gets the isolated storage file for the app.
        ///  note:
        /// GetUserStoreForDomain() works only after published with ClickOnce, otherwise GetUserStoreForDomain() must be used for debugging!
        /// </summary>
        /// <returns></returns>
        public static IsolatedStorageFile GetStorage()
        {
            try
            {
                return IsolatedStorageFile.GetUserStoreForApplication();
            }
            catch (IsolatedStorageException)
            {
                return IsolatedStorageFile.GetUserStoreForDomain();
            }

        }


    }
}
